package com.ruoyi.system.media;


import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.prefs.Preferences;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 工具类
 */
public class ZTools {
    /**
     * 字符串转整数
     *
     * @param str   - 数字开头的字符串(包含其他字符时只有前序数字参与计算)
     * @param radix - 进制, 2:二进制;8:八进制;10:十进制;16:十六进制
     * @param bLoop - true:可逾越,0xFFFFFFFF为-1; false:不逾越,返回值范围[-2147483648,2147483647]
     * @return
     */
    public static int a2i(String str, int radix, boolean bLoop) {
        if (str.isEmpty()) {
            return 0;
        }

        int ret = 0;
        boolean signed = false;

        if (str != null) {
            // 格式化(去除空格及转大写)
            str = str.trim();
            str = str.toUpperCase();
            if (str.isEmpty()) {
                return 0;
            }

            // 去除前导0
            while (str.length() > 0 && str.charAt(0) == '0') {
                str = str.substring(1);    //为0去除0
            }
            if (str.isEmpty()) {
                return 0;
            }

            // 负值判断
            if (str.charAt(0) == '-') {
                signed = true;            //为负号
                str = str.substring(1); //去除负号
            }
            if (str.isEmpty()) {
                return 0;
            }

            // 获取前导数字字符
            String tmp = "";
            for (int aa = 0; aa < str.length(); aa++) {
                char chr = str.charAt(aa);
                if (radix == 2) {
                    if (chr >= '0' && chr <= '1') {
                        tmp += chr;
                    } else {
                        break;
                    }
                } else if (radix == 8) {
                    if (chr >= '0' && chr <= '7') {
                        tmp += chr;
                    } else {
                        break;
                    }
                } else if (radix == 10) {
                    if (chr >= '0' && chr <= '9') {
                        tmp += chr;
                    } else {
                        break;
                    }
                } else if (radix == 16) {
                    if (chr >= '0' && chr <= '9') {
                        tmp += chr;
                    } else if (chr >= 'A' && chr <= 'F') {
                        tmp += chr;
                    } else {
                        break;
                    }
                }
            }
            if (tmp.isEmpty()) {
                return 0;
            }

            // 转换
            try {
                if (signed) {
                    tmp = "-" + tmp;
                }
                long aa = Long.parseLong(tmp, radix);
                if (bLoop) {// 可逾越循环
                    ret = (int) aa;
                } else {// 非逾越循环,限最大最小值
                    if (aa > Integer.MAX_VALUE)            //超最大值
                    {
                        ret = Integer.MAX_VALUE;    //取最大值
                    } else if (aa < Integer.MIN_VALUE)        //超最小值
                    {
                        ret = Integer.MIN_VALUE;    //取最小值
                    } else {
                        ret = (int) aa;
                    }
                }
            } catch (NumberFormatException e) {
                ret = 0;
            }
        }
        return ret;
    }

    /**
     * 字符串转整数
     *
     * @param str   - 数字开头的字符串(包含其他字符时只有前序数字参与计算)
     * @param radix - 进制, 2:二进制;8:八进制;10:十进制;16:十六进制
     * @return 逾越输出转换的数值
     */
    public static int a2i(String str, int radix) {
        return a2i(str, radix, true);
    }

    /**
     * 十进制字符串转整数
     *
     * @param s
     * @return
     */
    public static int a2i(String s) {
        return a2i(s, 10);
    }

    /**
     * 字符串转长整数
     *
     * @param str   - 数值字符串
     * @param radix - 进制
     * @return
     */
    public static long a2l(String str, int radix) {
        // 格式化(去除空格及转大写)
        str = str.trim();
        str = str.toUpperCase();
        if (str.isEmpty()) {
            return 0;
        }

        long ret = 0;
        boolean signed = false;

        if (str != null) {
            // 去除前导0
            while (str.length() > 0 && str.charAt(0) == '0') {
                str = str.substring(1);    //为0去除0
            }
            if (str.isEmpty()) {
                return 0;
            }

            // 负值判断
            if (str.charAt(0) == '-') {
                signed = true;            //为负号
                str = str.substring(1); //去除负号
            }
            if (str.isEmpty()) {
                return 0;
            }

            // 去除前导0
            while (str.length() > 0 && str.charAt(0) == '0') {
                str = str.substring(1);    //为0去除0
            }
            if (str.isEmpty()) {
                return 0;
            }


            // 获取前导数字字符
            String tmp = "";
            for (int aa = 0; aa < str.length(); aa++) {
                char chr = str.charAt(aa);
                if (radix == 2) {
                    if (chr >= '0' && chr <= '1') {
                        tmp += chr;
                    } else {
                        break;
                    }
                } else if (radix == 8) {
                    if (chr >= '0' && chr <= '7') {
                        tmp += chr;
                    } else {
                        break;
                    }
                } else if (radix == 10) {
                    if (chr >= '0' && chr <= '9') {
                        tmp += chr;
                    } else {
                        break;
                    }
                } else if (radix == 16) {
                    if (chr >= '0' && chr <= '9') {
                        tmp += chr;
                    } else if (chr >= 'A' && chr <= 'F') {
                        tmp += chr;
                    } else {
                        break;
                    }
                }
            }
            if (tmp.isEmpty()) {
                return 0;
            }

            // 转换
            try {
                ret = Long.parseUnsignedLong(tmp, radix);
                if (signed) {
                    ret = -ret;
                }
            } catch (NumberFormatException e) {
                ret = 0;
            }
        }
        return ret;
    }

    /**
     * 十进制字符串转长整数
     *
     * @param s
     * @return
     */
    public static long a2l(String s) {
        return a2l(s, 10);
    }

    /**
     * 整数转字符串
     *
     * @param val
     * @param radix  - 2:转二进制; 8:转八进制; 10:转十进制; 16:转十六进制
     * @param width  - 字符串长度, 不足前补flag
     * @param prefix
     * @return
     */
    public static String i2a(int val, int radix, int width, char prefix) {
        String res = "";
        StringBuffer sb = new StringBuffer();

        if (prefix == '\0') {
            prefix = ' ';
        }

        char[] szPrefix = new char[width + 1];
        for (int aa = 0; aa < szPrefix.length; aa++) {
            szPrefix[aa] = prefix;
        }

        if (radix == 2) {
            res = Integer.toBinaryString(val).toUpperCase();
            if (width > 0 && res.length() < width) {
                sb.append(szPrefix, 0, width - res.length());
                sb.append(res);
                res = sb.toString();
            }
        } else if (radix == 8) {
            res = Integer.toOctalString(val).toUpperCase();
            if (width > 0 && res.length() < width) {
                sb.append(szPrefix, 0, width - res.length());
                sb.append(res);
                res = sb.toString();
            }
        } else if (radix == 16) {
            res = Integer.toHexString(val).toUpperCase();
            if (width > 0 && res.length() < width) {
                sb.append(szPrefix, 0, width - res.length());
                sb.append(res);
                res = sb.toString();
            }
        } else if (radix == 10) {
            res = Integer.toString(val);
            if (val >= 0 && width > 0 && res.length() < width) {
                sb.append(szPrefix, 0, width - res.length());
                sb.append(res);
                res = sb.toString();
            }
        }
        return res;
    }

    /**
     * 整数转字符串
     *
     * @param val
     * @param radix - 2:转二进制; 8:转八进制; 10:转十进制; 16:转十六进制
     * @param width - 字符串长度, 不足前补'0'
     * @return
     */
    public static String i2a(int val, int radix, int width) {
        return i2a(val, radix, width, '0');
    }

    /**
     * 整数转字符串
     *
     * @param val
     * @param radix - 2:转二进制; 8:转八进制; 10:转十进制; 16:转十六进制
     * @return
     */
    public static String i2a(int val, int radix) {
        return i2a(val, radix, 0);
    }

    /**
     * 整数转十进制字符串
     *
     * @param val
     * @return
     */
    public static String i2a(int val) {
        return i2a(val, 10);
    }

    /**
     * 长整型转字符串
     *
     * @param val    - 值
     * @param radix  - 进制,2:二进制;8:八进制;10:十进制;16:十六进制
     * @param width  - 返回字符串的宽度
     * @param prefix - 返回字符串的前导符,默认为'0'
     * @return
     */
    public static String l2a(long val, int radix, int width, char prefix) {
        String res = "";
        StringBuffer sb = new StringBuffer();

        if (prefix == '\0') {
            prefix = ' ';
        }

        char[] szPrefix = new char[width + 1];
        for (int aa = 0; aa < szPrefix.length; aa++) {
            szPrefix[aa] = prefix;
        }

        if (radix == 2) {
            res = Long.toBinaryString(val).toUpperCase();
            if (width > 0 && res.length() < width) {
                sb.append(szPrefix, 0, width - res.length());
                sb.append(res);
                res = sb.toString();
            }
        } else if (radix == 8) {
            res = Long.toOctalString(val).toUpperCase();
            if (width > 0 && res.length() < width) {
                sb.append(szPrefix, 0, width - res.length());
                sb.append(res);
                res = sb.toString();
            }
        } else if (radix == 16) {
            res = Long.toHexString(val).toUpperCase();
            if (width > 0 && res.length() < width) {
                sb.append(szPrefix, 0, width - res.length());
                sb.append(res);
                res = sb.toString();
            }
        } else if (radix == 10) {
            res = Long.toString(val);
            if (val >= 0 && width > 0 && res.length() < width) {
                sb.append(szPrefix, 0, width - res.length());
                sb.append(res);
                res = sb.toString();
            }
        }
        return res;
    }

    /**
     * 长整型转字符串,默认前导符为'0'
     *
     * @param val   - 数值
     * @param radix - 进制,2:二进制;8:八进制;10:十进制;16:十六进制
     * @param width - 返回字符串的宽度
     * @return
     */
    public static String l2a(long val, int radix, int width) {
        return l2a(val, radix, width, '0');
    }

    /**
     * 长整型转字符串,无前导符
     *
     * @param val   - 数值
     * @param radix - 进制,2:二进制;8:八进制;10:十进制;16:十六进制
     * @return
     */
    public static String l2a(long val, int radix) {
        return l2a(val, radix, 0);
    }

    /**
     * 长整型转字符串(十进制、无前导符)
     *
     * @param val
     * @return
     */
    public static String l2a(long val) {
        return l2a(val, 10);
    }

    /**
     * 字符串转float
     *
     * @param num
     * @return
     */
    public static float a2f(String num) {
        float res = 0.0f;
        try {
            res = Float.valueOf(num);
        } catch (NumberFormatException e) {
        }
        return res;
    }

    /**
     * float转字符串
     *
     * @param num
     * @return
     */
    public static String f2a(float num) {
        String res = Float.toString(num);

        return res;
    }

    /**
     * float转字符串
     *
     * @param num
     * @return
     */
    public static String f2a(float num, String pattern) {
        DecimalFormat fmt = new DecimalFormat(pattern);
        return fmt.format(num);
    }

    /**
     * float转int
     *
     * @param num
     * @return
     */
    public static int f2i(float num) {
        return Math.round(num);
    }

    /**
     * 求百分比
     *
     * @return
     */
    public static float percent(float sub, float total) {
        if (total != 0) {
            return sub * 100 / total;
        }
        return 100.00f;
    }

    /**
     * 求百分比
     *
     * @return
     */
    public static float percent(int sub, int total) {
        if (total != 0) {
            return (float) sub * 100 / (float) total;
        }
        return 100.00f;
    }

    /**
     * 求百分比
     *
     * @return
     */
    public static float percent(long sub, long total) {
        if (total != 0) {
            return (float) sub * 100 / (float) total;
        }
        return 100.00f;
    }

    /**
     * 按位设置值
     *
     * @param bits
     * @return
     */
    public static int mkByte(int... bits) {
        int ret = 0;
        for (int bit : bits) {
            if (bit >= 0 && bit < 8) {
                ret |= (1 << bit) & 0xFF;
            }
        }
        return ret;
    }

    /**
     * 按位设置值
     *
     * @param bits
     * @return
     */
    public static int mkWord(int... bits) {
        int ret = 0;
        for (int bit : bits) {
            if (bit >= 0 && bit < 16) {
                ret |= (1 << bit) & 0xFFFF;
            }
        }
        return ret;
    }

    /**
     * 按位设置值
     *
     * @param bits
     * @return
     */
    public static long mkInt(int... bits) {
        long ret = 0;
        for (int bit : bits) {
            if (bit >= 0 && bit < 32) {
                ret |= (1 << bit) & 0xFFFFFFFFL;
            }
        }
        return ret;
    }

    /**
     * 按位设置变量的值
     *
     * @param val
     * @param bit
     * @param bSet
     * @return
     */
    public static int set8bit(int val, int bit, boolean bSet) {
        int aa = val & 0xFF;
        if (bit >= 0 && bit < 8) {
            int bb = (1 << bit) & 0xFF;
            if (bSet) {
                aa |= bb;
            } else {
                aa &= ((~bb) & 0xFF);
            }
        }
        return aa;
    }

    /**
     * 按位设置变量的值
     *
     * @param val
     * @param bit
     * @param bSet
     * @return
     */
    public static int set16bit(int val, int bit, boolean bSet) {
        int aa = val & 0xFFFF;
        if (bit >= 0 && bit < 16) {
            int bb = (1 << bit) & 0xFFFF;
            if (bSet) {
                aa |= bb;
            } else {
                aa &= ((~bb) & 0xFFFF);
            }
        }
        return aa;
    }

    /**
     * 按位设置变量的值
     *
     * @param val
     * @param bit
     * @param bSet
     * @return
     */
    public static long set32bit(long val, int bit, boolean bSet) {
        long aa = val & 0xFFFFFFFFL;
        if (bit >= 0 && bit < 32) {
            long bb = (1 << bit) & 0xFFFFFFFFL;
            if (bSet) {
                aa |= bb;
            } else {
                aa &= ((~bb) & 0xFFFFFFFFL);
            }
        }
        return aa;
    }

    /**
     * 测试相关位是否设置
     *
     * @param val
     * @param bit
     * @return
     */
    public static boolean tst32bit(long val, int bit) {
        long bb = (1L << bit) & 0xFFFFFFFFL;
        return (val & bb) != 0;
    }

    /**
     * 将字符转换为ascii码值
     *
     * @param c - 字符
     * @return
     */
    public static byte c2i(char c) {
        byte ascii = (byte) c;
        return ascii;
    }

    /**
     * 从右开始取子字符串
     *
     * @param src
     * @param nCount
     * @return
     */
    public static String right(String src, int nCount) {
        if (src.isEmpty()) {
            return "";
        }

        if (src.length() < nCount) {
            nCount = src.length();
        }

        return src.substring(src.length() - nCount);
    }

    /**
     * 从左开始取子字符串
     *
     * @param src
     * @param nCount
     * @return
     */
    public static String left(String src, int nCount) {
        if (src.isEmpty()) {
            return "";
        }

        if (src.length() < nCount) {
            nCount = src.length();
        }

        return src.substring(0, nCount);
    }

    /**
     * 从index开始取子字符串
     *
     * @param src
     * @param index
     * @return
     */
    public static String mid(String src, int index) {
        if (src.isEmpty()) {
            return "";
        }

        if (src.length() < (index + 1)) {
            return "";
        }

        return src.substring(index);
    }

    /**
     * 从index开始取nCount个字符
     *
     * @param src
     * @param index
     * @return
     */
    public static String mid(String src, int index, int nCount) {
        if (src.isEmpty()) {
            return "";
        }

        if (src.length() < (index + 1)) {
            return "";
        }

        if (src.length() < (index + nCount)) {
            nCount = src.length() - index;
        }

        return src.substring(index, index + nCount);
    }

    /**
     * 构造重复字符串,直接用separ进行分割
     */
    public static String strRepeat(int count, String src, String separ) {
        StringBuffer sb = new StringBuffer();
        for (int aa = 0; aa < count; aa++) {
            if (aa > 0) {
                sb.append(separ);
            }
            sb.append(src);
        }
        return sb.toString();
    }

    /**
     * 拆分字符串
     * 返回固定长度的字符串数组
     *
     * @param src
     * @param len   - 强制返回的数组长度
     * @param regex
     * @return
     */
    public static String[] split(String src, int len, String regex) {
        String[] ret = new String[len];
        for (int aa = 0; aa < len; aa++) {
            ret[aa] = "";
        }

        if (src != null && !src.isEmpty()) {
            String[] tmp = src.split(regex);

            for (int aa = 0; tmp != null && aa < tmp.length && aa < len; aa++) {
                ret[aa] = tmp[aa].trim();
            }
        }

        return ret;
    }

    /**
     * 将字符串数组合并为字符串
     *
     * @param hasDelimiter - true:必须有定界符; false:去除串首尾定界符
     * @param delimiter    - 定界符
     * @param strs         - 字符串数组
     * @return
     */
    public static String joinSTR(boolean hasDelimiter, String delimiter, String... strs) {
        StringBuffer ret = new StringBuffer(joinSTR(strs, delimiter));

        if (!hasDelimiter) {// 需要去除首尾定界符
            while (ret.indexOf(delimiter) == 0) {
                ret.deleteCharAt(0);
            }
            while (ret.length() > 0 && ret.lastIndexOf(delimiter) == (ret.length() - 1)) {
                ret.deleteCharAt(ret.length() - 1);
            }

        }
        return ret.toString();
    }

    /**
     * 字符串数组合并为字符串
     *
     * @param arr
     * @param delimiter
     * @param offset    - 开始索引位置(以0开始)
     * @return
     */
    public static String joinSTR(String[] arr, CharSequence delimiter, int offset) {
        StringBuffer ret = new StringBuffer();
        if (arr != null && arr.length > 0) {
            for (int aa = offset; aa < arr.length; aa++) {
                ret.append(String.format("%s", arr[aa]));
                if (aa < arr.length - 1) {
                    ret.append(delimiter);
                }
            }
        }
        return ret.toString();
    }

    /**
     * 字符串数组合并为字符串
     *
     * @param arr
     * @param delimiter
     * @return
     */
    public static String joinSTR(String[] arr, CharSequence delimiter) {
        return joinSTR(arr, delimiter, 0);
    }

    /**
     * 字符串数组合并为字符串
     *
     * @param arr
     * @return
     */
    public static String joinSTR(String[] arr) {
        return joinSTR(arr, "", 0);
    }

    /**
     * 字符串数组合并为字符串
     *
     * @param arr
     * @param maxcnt
     * @param delimiter
     * @return java.lang.String
     */
    public static String joinSTR(String[] arr, int maxcnt, CharSequence delimiter) {
        String[] dest = new String[maxcnt];
        for (int aa = 0; aa < maxcnt; aa++) {
            if (arr != null && aa < arr.length) {
                dest[aa] = String.format("%s", arr[aa]);
            } else {
                dest[aa] = "";
            }
        }
        return joinSTR(dest, delimiter, 0);
    }

    /**
     * 字符串数组合并为字符串
     *
     * @param arr
     * @return
     */
    public static String joinSTR(String[] arr, int maxcnt) {
        return joinSTR(arr, maxcnt, "");
    }

    public static String joinSTR(List<String> lst, CharSequence delimiter) {
        StringBuffer ret = new StringBuffer();
        if (lst != null) {
            for (int aa = 0; aa < lst.size(); aa++) {
                if (aa > 0) {
                    ret.append(delimiter);
                }
                ret.append(lst.get(aa));
            }
        }

        return ret.toString();
    }

    public static String joinSTR(List<String> lst) {
        return joinSTR(lst, ",");
    }

    public static String chkWinPath(String path) {
        StringBuffer ret = new StringBuffer();
        for (int aa = 0; path != null && aa < path.length(); aa++) {
            char cc = path.charAt(aa);
            if (cc != '\\' && cc != '/' && cc != '*' && cc != '?' && cc != '"' && cc != '<' && cc != '>' && cc != '|') {
                ret.append(cc);
            }
        }
        return ret.toString();
    }

    /**
     * 将二进制串转换为十六进制字符串
     */
    public static String bin2hex(byte[] sz, boolean lowerCase) {
        StringBuffer sb = new StringBuffer();
        for (int aa = 0; aa < sz.length; aa++) {
            if (lowerCase) {
                sb.append(String.format("%02x", sz[aa]));
            } else {
                sb.append(String.format("%02X", sz[aa]));
            }
        }

        return sb.toString();
    }

    /**
     * 将二进制串转换为十六进制字符串
     */
    public static String bin2hex(byte[] sz) {
        return bin2hex(sz, false);
    }

    /**
     * 将二进制串转换为十六进制字符串
     */
    public static String bin2hex(byte[] sz, int index, int len) {
        byte[] tmp = Arrays.copyOfRange(sz, index, index + len);
        return bin2hex(tmp);
    }

    /**
     * 将十六进制字符串 转换为二进制串
     */
    public static byte[] hex2bin(String hex) {
        if (hex.length() % 2 == 1) {
            hex = "0" + hex;
        }
        byte[] ret = new byte[hex.length() / 2];
        for (int i = 0; i < ret.length; i++) {
            String sub = hex.substring(i * 2, i * 2 + 2);
            try {
                ret[i] = (byte) Integer.parseInt(sub, 16);
            } catch (NumberFormatException e) {
                ret[i] = 0;
            }
        }
        return ret;
    }

    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }

    /**
     * 将HEX字符串转为byte数组
     *
     * @param len - 目标数组元素个数(数组尺寸)
     * @param hex - HEX字符串
     * @return
     */
    public static byte[] hex2bin(int len, String hex) {
        byte[] buff = hex2bin(hex);
        return Arrays.copyOf(buff, len);
    }

    /**
     * 字符串转字节数组
     *
     * @param str
     * @param len
     * @return
     */
    public static byte[] str2bin(int len, String str, String charsetName) {
        if (str != null) {
            try {
                byte[] strBytes = Arrays.copyOf(str.getBytes(charsetName), len);
                return Arrays.copyOf(strBytes, len);
            } catch (UnsupportedEncodingException e) {
            }
        }
        return new byte[len];
    }

    /**
     * 字符串转字节数组(采用DB2312编码格式)
     *
     * @param str
     * @param len
     * @return
     */
    public static byte[] str2bin(int len, String str) {
        return str2bin(len, str, "GB2312");
    }

    /**
     * 字符串转HEX字符串
     *
     * @param str
     * @param charsetName
     * @return
     */
    public static String str2hex(String str, String charsetName) {
        String hex = "";
        byte[] bin = str2Bytes(str, charsetName);

        hex = bin2hex(bin);

        return hex;
    }

    /**
     * 字符串转HEX字符串
     * 字符集为GB2312
     *
     * @param str
     * @return
     */
    public static String str2hex(String str) {
        return str2hex(str, "gb2312");
    }

    /**
     * HEX字符串转字符串
     *
     * @param strHex
     * @param charsetName
     * @return
     */
    public static String hex2str(String strHex, String charsetName) {
        String str = "";
        byte[] bin = hex2bin(strHex);
        str = bin2str(bin, charsetName);
        return str;
    }

    /**
     * HEX字符串转字符串
     * 默认采用GB2312编码
     *
     * @param strHex
     * @return
     */
    public static String hex2str(String strHex) {
        return hex2str(strHex, "gb2312");
    }

    /**
     * 构造byte数组
     *
     * @param byteValues
     * @return
     */
    public static byte[] toBytes(int... byteValues) {
        byte[] ret = new byte[byteValues.length];
        for (int aa = 0; aa < byteValues.length; aa++) {
            int val = byteValues[aa] & 0xFF;
            ret[aa] = (byte) val;
        }
        return ret;
    }


    /**
     * byte数组合并
     *
     * @param values
     * @return
     */
    public static byte[] sbMerger(byte[]... values) {
        int length_byte = 0;
        for (int i = 0; i < values.length; i++) {
            length_byte += values[i].length;
        }
        byte[] all_byte = new byte[length_byte];
        int countLength = 0;
        for (int i = 0; i < values.length; i++) {
            byte[] b = values[i];
            System.arraycopy(b, 0, all_byte, countLength, b.length);
            countLength += b.length;
        }
        return all_byte;
    }

    /**
     * 从源byte缓冲区指定位置开始复制len字节为新byte缓冲区
     */
    public static byte[] arrcopy(byte[] src, int index, int len) {
        if (index < src.length) {
            return Arrays.copyOfRange(src, index, index + len);
        }
        return new byte[len];
    }

    /**
     * 复制源byte缓冲区到目标byte缓冲区指定开始位置
     *
     * @param src
     * @param des
     * @param desIndex
     * @return
     */
    public static int arrcopy(byte[] src, byte[] des, int desIndex) {
        if (des != null && src != null) {
            for (int aa = 0; aa < src.length && desIndex < des.length; aa++) {
                des[desIndex] = src[aa];
                desIndex++;
            }
            return desIndex;
        }
        return 0;
    }

    /**
     * 复制源byte缓冲区指定内容到目标byte缓冲区中
     *
     * @param src
     * @param src - 源开始索引
     * @param des
     * @return - 复制的字节个数
     */
    public static int arrcopy(byte[] src, int srcIndex, byte[] des) {
        if (src != null && des != null) {
            int srcLen = src.length;
            int desLen = des.length;

            if (srcLen >= (srcIndex + desLen)) {
                System.arraycopy(src, srcIndex, des, 0, desLen);
                return desLen;
            } else {// 不让后续再进行复制操作
                return srcLen - 1;
            }
        }
        return 0;
    }

    /**
     * 二维数组转一维数组
     *
     * @param src
     * @return
     */
    public static byte[] twz2oneBytes(byte[][] src) {
        int len = 0, already = 0;
        for (byte[] el : src) {
            len += el.length;
        }
        byte[] des = new byte[len];
        for (byte[] el : src) {
            System.arraycopy(el, 0, des, already, el.length);
            already += el.length;
        }
        return des;
    }

    /**
     * 将int转为byte数组
     *
     * @param num
     * @param bigAtFront - true:大端在前; false-小端在前
     * @param len_bytes  - 目标数组长度(1~4bytes)
     * @return
     */
    public static byte[] int2bin(int len_bytes, long num, boolean bigAtFront) {

        if (len_bytes > 0 && len_bytes < 5) {
            byte[] sb = new byte[len_bytes];

            if (bigAtFront) {// 大端在前
                for (int aa = len_bytes; aa > 0; aa--) {
                    sb[aa - 1] = (byte) (num & 0xFF);
                    num = (num >> 8); // 向右移8位
                }
            } else {// 小端在前
                for (int a = 0; a < len_bytes; a++) {
                    sb[a] = (byte) (num & 0xFF);
                    num = (num >> 8); // 向右移8位
                }
            }
            return sb;
        }
        return null;
    }

    /**
     * 将int转为byte数组(大端在前)
     *
     * @param len_bytes - 目标数组长度(1~4bytes)
     * @param num
     * @return
     */
    public static byte[] int2bin(int len_bytes, long num) {
        return int2bin(len_bytes, num, true);
    }


    /**
     * 从ByteBuffer获取一个字节
     * 若已到缓冲区尾部抛出异常
     *
     * @param sb
     * @return
     * @throws Exception
     */
    public static int getByte(ByteBuffer sb) throws Exception {
        if (sb.remaining() == 0) {
            new Exception("EOF");
        }
        return sb.get() & 0xFF;
    }

    /**
     * 从ByteBuffer获取指定长度字节
     * 若已到缓冲区尾部抛出异常
     *
     * @param sb
     * @return
     * @throws Exception
     */
    public static void getBytes(ByteBuffer sb, byte[] dst) throws Exception {
        if (sb.remaining() < dst.length) {
            new Exception("EOF");
        }
        sb.get(dst);
    }

    /**
     * 获取C++ BYTE序列化内容
     *
     * @param sb
     * @return
     */
    public static int getCByte(ByteBuffer sb) throws Exception {
        return getByte(sb);
    }

    /**
     * 获取C++ UINT序列化内容
     *
     * @param sb
     * @return
     */
    public static long getCInt(ByteBuffer sb) throws Exception {
        long ll = getByte(sb);
        long lh = getByte(sb);
        long hl = getByte(sb);
        long hh = getByte(sb);

        return (hh << 24) + (hl << 16) + (lh << 8) + ll;
    }

    /**
     * 获取CString序列化内容
     *
     * @param sb
     * @return
     */
    public static String getCString(ByteBuffer sb) throws Exception {
        String ret = "";

        int mulit = 0;//倍数
        int mode = 0;    //余数
        int len = 0;    //字符串个数

        mode = getByte(sb);
        if (mode == 0xFF) {
            mode = getByte(sb);
            mulit = getByte(sb);
        }
        len = (mulit << 8) + mode;

        byte[] sz = new byte[len];
        getBytes(sb, sz);

        try {
            ret = new String(sz, "gbk");
        } catch (UnsupportedEncodingException e) {
        }

        return ret;
    }

    public static int random() {
        Random random = new Random(System.currentTimeMillis());

        return random.nextInt();
    }

    public static int random(int bound) {
        Random random = new Random(System.currentTimeMillis());

        return random.nextInt(bound);
    }


    /**
     * 将short转为byte数组(大端在前)
     *
     * @param num
     * @return
     */
    public static byte[] short2bin(int num) {
        return int2bin(2, num, true);
    }

    /**
     * 将byte数组转换为int数
     *
     * @param bigAtFront - true：大端在前；false：下端在前
     * @return
     */
    public static long bin2lng(byte[] sb, boolean bigAtFront) {
        if (sb == null || sb.length < 1) {
            return 0;
        }

        long ret = 0;
        if (!bigAtFront) {// 小端在前
            // 反转数组为大端在前
            byte[] tm = new byte[sb.length];
            for (int aa = 0; aa < sb.length; aa++) {
                tm[aa] = sb[sb.length - aa - 1];
            }
            sb = tm;
        }
        try {
            ret = Long.parseUnsignedLong(bin2hex(sb), 16);
        } catch (NumberFormatException e) {
            // 数组超长
            ret = 0;
        }

        return ret;
    }

    /**
     * 将byte数组转换为int数(大端在前)
     *
     * @return
     */
    public static long bin2lng(byte[] intBytes) {
        return bin2lng(intBytes, true);
    }

    /**
     * 将byte数组转换为int数
     *
     * @param bigAtFront - true：大端在前；false：小端在前
     * @return
     */
    public static int bin2int(byte[] sb, boolean bigAtFront) {
        if (sb == null || sb.length == 0) {
            return 0;
        }

        int ret = 0;
        if (!bigAtFront) {// 小端在前

            // 反转数组为大端在前
            byte[] tm = new byte[sb.length];
            for (int aa = 0; aa < sb.length; aa++) {
                tm[aa] = sb[sb.length - aa - 1];
            }
            sb = tm;
        }
        try {
            ret = Integer.parseUnsignedInt(bin2hex(sb), 16);
        } catch (NumberFormatException e) {
            // 数组超长
            ret = 0;
        }
        return ret;
    }

    /**
     * 将byte数组转换为int数(大端在前)
     *
     * @return
     */
    public static int bin2int(byte[] intBytes) {
        return bin2int(intBytes, true);
    }

    /**
     * 将byte数组转换为int数(大端在前)
     *
     * @return
     */
    public static int bin2int(byte[] src, int index, int len) {
        return bin2int(arrcopy(src, index, len));
    }

    /**
     * BCD转int
     *
     * @param bcdBytes
     * @return
     */
    public static int bcd2int(byte[] bcdBytes) {
        String sNum = bin2hex(bcdBytes);
        if (!sNum.isEmpty()) {
            return ZTools.a2i(sNum);
        }
        return 0;
    }

    /**
     * int转bcd
     *
     * @param num
     * @return
     */
    public static byte[] int2bcd(long num) {
        String src = String.format("%d", num);
        if ((src.length() % 2) != 0) {
            src = "0" + src;
        }

        return hex2bin(src.length() / 2, src);
    }

    /**
     *	返回当前UNIX时间
     *	本地时区
     * @return
     */
//	public static long now2ut()
//	{
//		long ut = new Date().getTime()/1000;
//		return ut;
//	}

    /**
     * Date时间转UNIX时间
     * 本地时区
     *
     * @param date
     * @return
     */
    public static long dt2ut(Date date) {
        return date.getTime() / 1000;
    }

    /**
     * UNIX时间转Date
     *
     * @param ut
     * @return
     */
    public static Date ut2dt(long ut) {
        return new Date(ut * 1000);
    }

    /**
     * 按默认格式返回当前日期时间
     * 默认时间格式为"yyyy-MM-dd HH:mm:ss"
     *
     * @return
     */
    public static String now2str() {
        return now2str("yyyy-MM-dd HH:mm:ss", "GMT+08:00");
    }

    /**
     * 按给定格式返回当前日期时间
     *
     * @param pattern - 日期时间格式,如"yyyy-MM-dd HH:mm:ss"
     * @return
     */
    public static String now2str(String pattern) {
        return now2str(pattern, "GMT+08:00");
    }

    /**
     * 按给定格式返回当前日期时间
     *
     * @param pattern - 日期时间格式,如"yyyy-MM-dd HH:mm:ss"
     * @return
     */
    public static String now2str(String pattern, String timeZone) {
        String ret = "";
        if (pattern == null || pattern == "") {
            pattern = "yyyy-MM-dd HH:mm:ss";
        }
        if (timeZone == null || timeZone == "") {
            timeZone = "GMT+08:00";
        }

        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        sdf.setTimeZone(TimeZone.getTimeZone(timeZone));

        ret = sdf.format(new Date());

        return ret;
    }


    /**
     * 输出十进制日期小时
     *
     * @param date
     * @param pattern - 只能到小时,不能有"-"
     * @return - yyyyMMddHH
     */
    public static int dt2int(Date date, String pattern) {
        //2020020201
        String str = dt2str(date, pattern);
        return ZTools.a2i(str);
    }

    /**
     * 按给默认格式返回日期时间
     * 格式:"yyyy-MM-dd HH:mm:ss"
     *
     * @param date - 给定日期时间
     * @return - 输出格式："yyyy-MM-dd HH:mm:ss"
     */
    public static String dt2str(Date date) {
        return dt2str(date, "yyyy-MM-dd HH:mm:ss", "GMT+08:00");
    }

    /**
     * 按给定格式返回日期时间
     *
     * @param date    - 给定日期时间
     * @param pattern - 输出格式
     * @return
     */
    public static String dt2str(Date date, String pattern) {
        return dt2str(date, pattern, "GMT+08:00");
    }

    /**
     * 按给定格式返回日期时间
     *
     * @param date     - 给定日期时间
     * @param pattern  - 输出格式
     * @param timeZone - 时区，如："GMT+08:00"为北京时间
     * @return
     */
    public static String dt2str(Date date, String pattern, String timeZone) {
        String ret = "";
        if (pattern == null || pattern == "") {
            pattern = "yyyy-MM-dd HH:mm:ss";
        }
        if (timeZone == null || timeZone == "") {
            timeZone = "GMT+08:00";
        }

        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        sdf.setTimeZone(TimeZone.getTimeZone(timeZone));

        ret = sdf.format(date);

        return ret;
    }

    /**
     * 字符串时间转Date时间
     *
     * @param st       - 字符串时间
     * @param pattern  - 如"yyyy-MM-dd HH:mm:ss"或"yyyy-MM-dd HH:mm:ss SSS"
     * @param timeZone - 时区，如："GMT+08:00"为北京时间
     * @return
     */
    public static Date str2dt(String st, String pattern, String timeZone) {
        Date date = new Date(0);
        if (pattern == null || pattern == "") {
            pattern = "yyyy-MM-dd HH:mm:ss";
        }
        if (timeZone == null || timeZone == "") {
            timeZone = "GMT+08:00";
        }

        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        sdf.setTimeZone(TimeZone.getTimeZone(timeZone));

        try {//必须捕获异常
            date = sdf.parse(st);
        } catch (ParseException px) {
        }
        return date;
    }

    /**
     * 获取默认格式字符串时间的格式
     *
     * @param stDefault - 默认格式字符串时间, 默认格式：
     *                  <li>"yyyy-MM-dd HH:mm:ss"
     *                  <li>"yyyy-MM-dd HH:mm:ss.SSS"
     *                  <li>"yyyy-MM-ddTHH:mm:ss"
     *                  <li>"yyyy-MM-ddTHH:mm:ss.SSS"
     * @return - 默认时间格式
     */
    static String defaultPattern(String stDefault) {
        String pattern = "";
        stDefault = stDefault.toUpperCase();
        if (stDefault.indexOf("T") >= 0) {// 含T
            if (stDefault.indexOf(".") >= 0) {// 含毫秒时间
                pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS";
            } else {// 不含毫秒时间
                pattern = "yyyy-MM-dd'T'HH:mm:ss";
            }
        } else {// 不含T
            if (stDefault.indexOf(".") >= 0) {// 含毫秒时间
                pattern = "yyyy-MM-dd HH:mm:ss.SSS";
            } else {// 不含毫秒时间
                pattern = "yyyy-MM-dd HH:mm:ss";
            }
        }
        return pattern;
    }

    /**
     * 字符串时间转Date时间
     *
     * @param st      - 字符串时间
     * @param pattern - 如"yyyy-MM-dd HH:mm:ss"或"yyyy-MM-dd HH:mm:ss.SSS"
     * @return
     */
    public static Date str2dt(String st, String pattern) {
        return str2dt(st, pattern, "GMT+08:00");
    }

    /**
     * 默认格式字符串时间转Date时间
     *
     * @param st - 默认格式字符串时间, 默认格式:
     *           <li>"yyyy-MM-dd HH:mm:ss"
     *           <li>"yyyy-MM-dd HH:mm:ss.SSS"
     *           <li>"yyyy-MM-ddTHH:mm:ss"
     *           <li>"yyyy-MM-ddTHH:mm:ss.SSS"
     * @return
     */
    public static Date str2dt(String st) {
        return str2dt(st, defaultPattern(st));
    }

    /**
     * 测试字符串日期时间格式
     *
     * @param st
     * @param pattern
     * @return
     * @apiNote - 用DateEx.chkValid(String st,String pattern)替代
     */
    @Deprecated
    public static boolean tstDate(String st, String pattern) {
        Date dt = str2dt(st, pattern);
        return dt.compareTo(new Date(0)) != 0;
    }

    /**
     * 字符串时间转毫秒时间(1970-1-1 0:0:0距今的毫秒数)
     *
     * @param st      - 字符串时间
     * @param pattern - 如"yyyy-MM-dd HH:mm:ss"或"yyyy-MM-dd HH:mm:ss SSS"
     * @return
     */
    public static long str2ms(String st, String pattern) {
        return str2dt(st, pattern).getTime();
    }

    /**
     * 字符串时间转UNIX时间(1970-1-1 0:0:0距今的秒数)
     *
     * @param st      - 字符串时间
     * @param pattern - 如"yyyy-MM-dd HH:mm:ss"或"yyyy-MM-dd HH:mm:ss SSS"
     * @return
     */
    public static long str2ut(String st, String pattern) {
        return (str2dt(st, pattern).getTime() / 1000);
    }

    /**
     * 默认格式字符串时间转UNIX时间(1970-1-1 0:0:0距今的秒数)
     *
     * @param st - 默认格式字符串时间
     *           <li>"yyyy-MM-dd HH:mm:ss"
     *           <li>"yyyy-MM-dd HH:mm:ss.SSS"
     *           <li>"yyyy-MM-ddTHH:mm:ss"
     *           <li>"yyyy-MM-ddTHH:mm:ss.SSS"
     * @return
     */
    public static long str2ut(String st) {
        return str2ut(st, defaultPattern(st));
    }

    /**
     * unix时间转为字符串时间
     *
     * @param ut       - 1970-01-01 00:00:00 以来的秒数
     * @param pattern  - 如"yyyy-MM-dd HH:mm:ss"或"yyyy-MM-dd HH:mm:ss.SSS"
     * @param timeZone - 时区，如："GMT+08:00"为北京时间
     * @return
     */
    public static String ut2str(int ut, String pattern, String timeZone) {
        String ret = "";
        if (pattern == null || pattern == "") {
            pattern = "yyyy-MM-dd HH:mm:ss";
        }
        if (timeZone == null || timeZone == "") {
            timeZone = "GMT+08:00";
        }

        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        sdf.setTimeZone(TimeZone.getTimeZone(timeZone));

        long milliseconds = Integer.toUnsignedLong(ut) * 1000;
        ret = sdf.format(new Date(milliseconds));

        return ret;
    }

    /**
     * unix时间转为"yyyy-MM-dd HH:mm:ss"格式的字符串时间
     *
     * @param ut - 1970-01-01 00:00:00 以来的秒数
     * @return - 输出格式："yyyy-MM-dd HH:mm:ss"
     */
    public static String ut2str(int ut) {

        return ut2str(ut, "yyyy-MM-dd HH:mm:ss", "GMT+08:00");
    }

    /**
     * unix时间转为字符串时间
     *
     * @param ut      - 1970-01-01 08:00:00 以来的秒数
     * @param pattern - 输出格式
     * @return
     */
    public static String ut2str(int ut, String pattern) {

        return ut2str(ut, pattern, "GMT+08:00");
    }

    /**
     * Calendar转字符串时间
     *
     * @param cale
     * @param pattern - 如"yyyy-MM-dd HH:mm:ss"或"yyyy-MM-dd HH:mm:ss SSS"
     * @return
     */
    public static String cal2str(Calendar cale, String pattern) {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.CHINA);
        String dateStr = sdf.format(cale.getTime());

        return dateStr;
    }

    /**
     * 字符串时间转Calendar
     *
     * @param st
     * @param pattern - 如"yyyy-MM-dd HH:mm:ss"或"yyyy-MM-dd HH:mm:ss SSS"
     * @return
     */
    public static Calendar str2cal(String st, String pattern) {
        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.CHINA);

        Date date = new Date(0);
        try {
            date = sdf.parse(st);
        } catch (ParseException e) {
        }
        calendar.setTime(date);

        return calendar;
    }

    /**
     * 获取源时间前后xxx年的时间
     *
     * @param dt    - 源时间
     * @param years - 相差年数（正数为某日期之后；负数为某日期之前）
     * @return
     */
    public static Date someYear(Date dt, int years) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(dt);
        calendar.add(Calendar.YEAR, years);
        return calendar.getTime();
    }

    /**
     * 获取默认格式源字符串时间前后xxx年的时间
     *
     * @param st    - 默认格式源字符串时间, 默认格式：
     *              <li>"yyyy-MM-dd HH:mm:ss"
     *              <li>"yyyy-MM-dd HH:mm:ss.SSS"
     *              <li>"yyyy-MM-ddTHH:mm:ss"
     *              <li>"yyyy-MM-ddTHH:mm:ss.SSS"
     * @param years - 相差年数（正数为某日期之后；负数为某日期之前）
     * @return - 输出格式与源字符串格式相同
     */
    public static String someYear(String st, int years) {
        Date dt = str2dt(st);
        dt = someYear(dt, years);

        return dt2str(dt, defaultPattern(st));
    }

    /**
     * 获取源时间前后xxx月的时间
     *
     * @param dt     - 源时间
     * @param months - 相差月数（正数为某日期之后；负数为某日期之前）
     * @return
     */
    public static Date someMonth(Date dt, int months) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(dt);
        calendar.add(Calendar.MONTH, months);
        return calendar.getTime();
    }

    /**
     * 获取默认格式源字符串时间前后xxx月的时间
     *
     * @param st     - 默认格式源字符串时间, 默认格式：
     *               <li>"yyyy-MM-dd HH:mm:ss"
     *               <li>"yyyy-MM-dd HH:mm:ss.SSS"
     *               <li>"yyyy-MM-ddTHH:mm:ss"
     *               <li>"yyyy-MM-ddTHH:mm:ss.SSS"
     * @param months - 相差月数（正数为某日期之后；负数为某日期之前）
     * @return - 输出格式与源字符串格式相同
     */
    public static String someMonth(String st, int months) {
        Date dt = str2dt(st);
        dt = someMonth(dt, months);

        return dt2str(dt, defaultPattern(st));
    }

    /**
     * 获取源时间前后xxx天的时间
     *
     * @param dt   - 源时间
     * @param days - 相差天数（正数为某日期之后；负数为某日期之前）
     * @return
     */
    public static Date someDay(Date dt, int days) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(dt);
        calendar.add(Calendar.DATE, days);
        return calendar.getTime();
    }

    /**
     * 获取源时间前后xxx天的时间
     *
     * @param dt      - 源时间
     * @param days    - 相差天数（正数为某日期之后；负数为某日期之前）
     * @param pattern - 输出格式 :yyyy-MM-dd HH:mm:ss.SSS
     * @return
     */
    public static String someDay(Date dt, int days, String pattern) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(dt);
        calendar.add(Calendar.DATE, days);
        Date day = calendar.getTime();
        return dt2str(day, pattern);
    }

    /**
     * 获取默认格式源字符串时间前后xxx天的时间
     *
     * @param st      - 默认格式源字符串时间
     *                <li>"yyyy-MM-dd HH:mm:ss"
     *                <li>"yyyy-MM-dd HH:mm:ss.SSS"
     *                <li>"yyyy-MM-ddTHH:mm:ss"
     *                <li>"yyyy-MM-ddTHH:mm:ss.SSS"
     * @param days    - 相差天数（正数为某日期之后；负数为某日期之前）
     * @param pattern - 输出格式
     * @return
     */
    public static String someDay(String st, int days, String pattern) {
        Date dt = str2dt(st);
        dt = someDay(dt, days);
        return dt2str(dt, pattern);
    }

    /**
     * 获取默认格式源字符串时间前后xxx天的时间
     *
     * @param st   - 默认格式源字符串时间
     *             <li>"yyyy-MM-dd HH:mm:ss"
     *             <li>"yyyy-MM-dd HH:mm:ss.SSS"
     *             <li>"yyyy-MM-ddTHH:mm:ss"
     *             <li>"yyyy-MM-ddTHH:mm:ss.SSS"
     * @param days - 相差天数（正数为某日期之后；负数为某日期之前）
     * @return - 输出格式与源字符串格式相同
     */
    public static String someDay(String st, int days) {
        return someDay(st, days, defaultPattern(st));
    }

    /**
     * 获取源时间前后xxx秒的时间
     *
     * @param dt      - 源时间
     * @param seconds - 相差秒数（正数为某日期之后；负数为某日期之前）
     * @return
     */
    public static Date someTime(Date dt, int seconds) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(dt);
        calendar.add(Calendar.SECOND, seconds);
        return calendar.getTime();
    }

    /**
     * 获取源时间前后xxx秒的时间
     *
     * @param dt      - 源时间
     * @param seconds - 相差秒数（正数为某日期之后；负数为某日期之前）
     * @param pattern -输出格式
     * @return
     */
    public static String someTime(Date dt, int seconds, String pattern) {
        Date des = someTime(dt, seconds);
        return dt2str(des, pattern);
    }

    /**
     * 获取默认格式源字符串时间前后xxx秒的时间
     *
     * @param st      - 默认格式源字符串时间, 默认格式：
     *                <li>"yyyy-MM-dd HH:mm:ss"
     *                <li>"yyyy-MM-dd HH:mm:ss.SSS"
     *                <li>"yyyy-MM-ddTHH:mm:ss"
     *                <li>"yyyy-MM-ddTHH:mm:ss.SSS"
     * @param seconds - 相差秒数（正数为某日期之后；负数为某日期之前）
     * @param pattern - 输出格式
     * @return
     */
    public static String someTime(String st, int seconds, String pattern) {
        Date dt = str2dt(st);
        dt = someTime(dt, seconds);
        return dt2str(dt, pattern);
    }

    /**
     * 获取默认格式源字符串时间前后xxx秒的时间
     *
     * @param st      - 默认格式源字符串时间, 默认格式：
     *                <li>"yyyy-MM-dd HH:mm:ss"
     *                <li>"yyyy-MM-dd HH:mm:ss.SSS"
     *                <li>"yyyy-MM-ddTHH:mm:ss"
     *                <li>"yyyy-MM-ddTHH:mm:ss.SSS"
     * @param seconds - 相差秒数（正数为某日期之后；负数为某日期之前）
     * @return - 输出格式与源字符串格式相同
     */
    public static String someTime(String st, int seconds) {
        return someTime(st, seconds, defaultPattern(st));
    }

    /**
     * 获取源时间前后xxx秒的时间
     *
     * @param dt      - 源时间
     * @param seconds - 相差秒数（正数为某日期之后；负数为某日期之前）
     * @return - 输出格式：yyyy-MM-dd HH:mm:ss
     */
    public static String someTimeEx(Date dt, int seconds) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(dt);
        calendar.add(Calendar.SECOND, seconds);
        return dt2str(calendar.getTime());
    }

    /**
     * 获取默认格式源字符串时间前后xxx秒的时间
     *
     * @param st      - 默认格式源字符串时间, 默认格式：
     *                <li>"yyyy-MM-dd HH:mm:ss"
     *                <li>"yyyy-MM-dd HH:mm:ss.SSS"
     *                <li>"yyyy-MM-ddTHH:mm:ss"
     *                <li>"yyyy-MM-ddTHH:mm:ss.SSS"
     * @param seconds - 相差秒数（正数为某日期之后；负数为某日期之前）
     * @return
     */
    public static Date someTimeEx(String st, int seconds) {
        Date dt = str2dt(st);
        return dt = someTime(dt, seconds);
    }

    /**
     * 获取dt时间的子夜时间
     *
     * @param dt
     * @return
     */
    public static Date dt2Mid(Date dt) {
        String strMid = dt2str(dt, "yyyy-MM-dd 00:00:00");

        return str2dt(strMid);
    }

    /**
     * 获取当天子夜过secs秒后的时间
     *
     * @param secs
     * @return
     */
    @SuppressWarnings("deprecation")
    public static Date dtAfMid(int secs) {
        Date cur = new Date();
        Date mid = new Date(cur.getYear(), cur.getMonth(), cur.getDate());
        Date ret = new Date(mid.getTime() + secs * 1000L);
        return ret;
    }

    /**
     * 获取子夜至date的秒数
     *
     * @param date
     * @return
     */
    @SuppressWarnings("deprecation")
    public static int timesAfMid(Date date) {
        return date.getHours() * 3600 + date.getMinutes() * 60 + date.getSeconds();
    }

    /**
     * 计算间隔时间
     *
     * @param start
     * @param end
     * @return long[0]:相差总天数
     * long[1]:相差总小时数
     * long[2]:相差总分钟数
     * long[3]:相差总秒数
     */
    public static long[] timeInterval(Date start, Date end) {
        long timesDis = Math.abs(start.getTime() - end.getTime());
        long day = timesDis / (1000 * 60 * 60 * 24);
        long hour = timesDis / (1000 * 60 * 60);
        long min = timesDis / (1000 * 60);
        long sec = timesDis / 1000;
        return new long[]{day, hour, min, sec};
    }

    /**
     * 计算间隔时间
     *
     * @param start
     * @param end
     * @param type  - 0:间隔总天数; 1:间隔总时数; 2:间隔总分钟数; 3:间隔总秒数
     * @return
     */
    public static long timeInterval(Date start, Date end, int type) {
        long[] spans = timeInterval(start, end);
        if (type < 4) {
            return spans[type];
        }

        return spans[3];
    }

    /**
     * 计算间隔时间
     *
     * @param start
     * @param end
     * @param type  - 0:间隔总天数; 1:间隔总时数; 2:间隔总分钟数; 3:间隔总秒数
     * @return
     */
    public static long timeInterval(String start, String end, int type) {
        return timeInterval(str2dt(start), str2dt(end), type);
    }


    /**
     * 往控制台打印信息
     *
     * @param a
     */
    public static void println(int a) {
        System.out.println(a);
    }

    /**
     * 往控制台打印信息
     *
     * @param a
     */
    public static void println(long a) {
        System.out.println(a);
    }

    /**
     * 往控制台打印信息
     *
     * @param format
     * @param args
     */
    public static void println(String format, Object... args) {
        print(true, format, args);
    }

    /**
     * 往控制台打印信息
     *
     * @param newLine
     * @param format
     * @param args
     */
    public static void print(boolean newLine, String format, Object... args) {
        String msg = String.format(format, args);

        if (newLine) {
            System.out.println(msg);
        } else {
            System.out.print(msg);
        }
    }

    /**
     * 将ByteBuffer转换为byte[]
     *
     * @param bytes
     * @return
     */
    public static byte[] buf2arr(ByteBuffer bytes) {
        int len = bytes.limit() - bytes.position();
        byte[] bytes1 = new byte[len];
        bytes.get(bytes1);
        return bytes1;
    }

    /**
     * 计算MD5码
     *
     * @return
     */
    public static String md5(byte[] input) {
        String md5code = null;
        byte[] secretBytes = null;
        try {
            // 拿到一个MD5转换器（如果想要SHA1参数换成”SHA1”）
            MessageDigest md = MessageDigest.getInstance("MD5");
            // Updates the digest using the specified array of bytes
            // md.update(input);
            // Completes the hash computation by performing final operations
            // such as padding. The digest is reset after this call is made.
            secretBytes = md.digest(input);
            md5code = ZTools.bin2hex(secretBytes);
            /**另一种转换为字符串的方法
             // 通过BigInteger进行转换（MD5码长度为16字节）
             md5code = new BigInteger(1, secretBytes).toString(16);
             // 不满32位前补0
             for (int i = 0; i < 32 - md5code.length(); i++) {
             md5code = "0" + md5code;
             }*/

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("无MD5码算法");
        }
        return md5code;
    }

    /**
     * 计算MD5码
     *
     * @return
     */
    public static String md5(String str, String charset) {
        String ret = null;

        try {
            ret = md5(str.getBytes(charset));
        } catch (RuntimeException | UnsupportedEncodingException e) {
            ret = "";
        }

        return ret;
    }

    /**
     * 利用java原生的类实现SHA256加密
     *
     * @param input 加密前的报文
     * @return
     */
    public static String sha256(byte[] input) {
        MessageDigest messageDigest;
        String encodestr = "";
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(input);
            encodestr = ZTools.bin2hex(messageDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            //e.printStackTrace();
        }
        return encodestr;
    }

    /**
     * base64 编码
     *
     * @param szBytes
     * @return
     */
    public static String encodeBase64(byte[] szBytes) {
        //byte[] textByte = text.getBytes("UTF-8");
        String encode = "";
        try {
            Base64.Encoder encoder = Base64.getEncoder();
            encode = encoder.encodeToString(szBytes);
        } catch (Exception e) {
        }
        return encode;
    }

    /**
     * base64 解码
     *
     * @param base64
     * @return
     */
    public static byte[] decodBase64(String base64) {
        byte[] decode = null;
        try {
            Base64.Decoder decoder = Base64.getDecoder();
            decode = decoder.decode(base64);
        } catch (Exception e) {
        }
        return decode;
    }

    /**
     * 字符串转字节数组
     *
     * @param str
     * @param charsetName - 字符串的编码格式
     * @return
     */
    public static byte[] str2Bytes(String str, String charsetName) {
        byte[] ret = null;
        try {
            ret = str.getBytes(charsetName);
        } catch (UnsupportedEncodingException | NullPointerException e) {
            ret = new byte[1];
        }
        return ret;
    }

    /**
     * 字节数组转字符串
     *
     * @param content
     * @param charsetName - 字节数组中信息的编码格式
     * @return
     */
    public static String bin2str(byte[] content, String charsetName) {
        String ret = "";
        if (content != null) {
            try {
                ret = new String(content, charsetName);
                ret = ret.trim();
            } catch (UnsupportedEncodingException e) {
                ret = "";
            }
        }
        return ret;
    }

    /**
     * 字节数组转字符串
     *
     * @param content - GB2312编码格式
     * @return
     */
    public static String bin2str(byte[] content) {
        return bin2str(content, "GB2312");
    }

    /**
     * 字节数组转字符串
     *
     * @param src
     * @param index
     * @param len
     * @return
     */
    public static String bin2str(byte[] src, int index, int len) {
        return bin2str(arrcopy(src, index, len));
    }

    /**
     * 获取子字符串
     *
     * @param src
     * @param staIndex - 开始位置索引值, 以0开始索引
     * @param len      - 长度
     * @return
     */
    public static String substr(String src, int staIndex, int len) {
        try {
            return src.substring(staIndex, staIndex + len);
        } catch (Exception e) {
        }
        return "";
    }

    /**
     * 通过具有分组的正则表达式取子字符串
     * 样例:subString("fullblk333366603.db3","(^[a-zA-Z]{1,})([0-9]{1,})(\\.db3)",0) 返回:"fullblk333366603.db3"
     * 样例:subString("fullblk333366603.db3","(^[a-zA-Z]{1,})([0-9]{1,})(\\.db3)",1) 返回:"fullblk"
     * 样例:subString("fullblk333366603.db3","(^[a-zA-Z]{1,})([0-9]{1,})(\\.db3)",2) 返回:"333366603"
     * 样例:subString("fullblk333366603.db3","(^[a-zA-Z]{1,})([0-9]{1,})(\\.db3)",3) 返回:".db3"
     *
     * @param src   - 源字符串
     * @param regex - 分组正则表达式
     * @param index - 需要返回的分组索引,由0开始编码,0索引源字符串
     * @return
     * @apiNote
     */
    public static String substr(String src, String regex, int index) {
        try {
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(src);
            if (matcher.find()) {// 发现匹配字符串
                if (matcher.groupCount() >= index) {
                    return matcher.group(index);
                }
            }
        } catch (Exception e) {
        }
        return "";
    }

    /**
     * 从CharBuffer中获取指定长度字符串
     *
     * @param buf
     * @param len
     * @return
     */
    public static String substr(CharBuffer buf, int len) {
        char[] sub = null;
        if (len > 0) {
            sub = new char[len];
        } else {
            sub = new char[buf.remaining()];
        }
        try {
            buf.get(sub);
        } catch (BufferUnderflowException e) {
        }
        return new String(sub);
    }

    /**
     * 分组返回匹配的子字符串
     * 2022-04-17
     *
     * @param src   - 源字符串
     * @param regex - 正则表达式
     * @param len   - 返回的字符串数组长度
     * @return
     */
    public static String[] matcher(String src, String regex, int len) {
        String[] res = new String[len];
        for (int aa = 0; aa < len; aa++) {
            res[aa] = "";
        }
        try {
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(src);
            if (matcher.find()) {// 发现匹配字符串
                int count = matcher.groupCount();
                for (int aa = 0; aa < count && aa < len; aa++) {
                    res[aa] = matcher.group(aa + 1);
                }
            }
        } catch (Exception e) {
        }

        return res;
    }

    /**
     * 从配置库中读取字符串信息
     * window为注册表，linux为配置文件
     *
     * @param c
     * @param key
     * @param def
     * @return
     */
    public static String regGetStr(Class<?> c, String key, String def) {
        Preferences reg = Preferences.userNodeForPackage(c);

        return reg.get(key, def);
    }

    /**
     * 从配置库中读取整形数信息
     * window为注册表，linux为配置文件
     *
     * @param c
     * @param key
     * @param def
     * @return
     */
    public static int regGetInt(Class<?> c, String key, int def) {
        Preferences reg = Preferences.userNodeForPackage(c);

        return reg.getInt(key, def);
    }

    /**
     * 往配置库写字符串信息
     * window为注册表，linux为配置文件
     *
     * @param c
     * @param key
     * @param value
     */
    public static void regPutStr(Class<?> c, String key, String value) {
        Preferences reg = Preferences.userNodeForPackage(c);
        reg.put(key, value);
    }

    /**
     * 往配置库中写整形数信息
     * window为注册表，linux为配置文件
     *
     * @param c
     * @param key
     * @param value
     */
    public static void regPutInt(Class<?> c, String key, int value) {
        Preferences reg = Preferences.userNodeForPackage(c);
        reg.putInt(key, value);
    }

    /**
     * 检查日期的合法信息
     */
    public static boolean chkDate(String st, String pattern) {
        Date date = new Date(0);
        Date temp = ZTools.str2dt(st, pattern);
        return date.getTime() != temp.getTime();
    }

    /**
     * 有效期判断
     */
    public static int compSDate(String dt1, String dt2) {
        return dt1.compareToIgnoreCase(dt2);
    }

    /**
     * HEX字符串合法性检验
     */
    public static boolean chkHexChar(String hex) {
        for (int i = 0; i < hex.length(); i++) {
            char chr = hex.charAt(i);
            if ((chr >= '0' && chr <= '9') || (chr >= 'a' && chr <= 'f') || (chr >= 'A' && chr <= 'F')) {
                continue;
            } else {
                return false;
            }
        }

        return true;
    }

    /**
     * 检查给定数是否是系列数之一
     */
    public static boolean isNumIn(int number, int... numbers) {
        for (int num : numbers) {
            if (number == num) {
                return true;
            }
        }
        return false;
    }

    /**
     * 检查给定数是否是常量之一
     */
    public static <T> boolean isNumIn(int number, Class<T> clazz) {
        ArrayList<Integer> lstVals = new ArrayList<>();

        // 获取类及其父类的公共属性
        Field[] fields = clazz.getFields();

        // 获取常量值
        try {
            T obj = clazz.getDeclaredConstructor().newInstance();

            for (Field field : fields) {
                //字段修饰检测
                //PUBLIC:1; PRIVATE:2; PROTECTED:4; STATIC: 8; FINAL:16
                if (field.getModifiers() != 25) {
                    continue; //只获取用public static final修饰的常量
                }

                //字段类型检测
                if (field.getType().getName().equals("int") || field.getType().getName().equals("Integer")) {
                    try {
                        Integer val = (Integer) field.get(obj);
                        lstVals.add(val);
                    } catch (IllegalArgumentException | IllegalAccessException e) {
                        //return false;
                    }
                }
            }
        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
                 InvocationTargetException | NoSuchMethodException | SecurityException e1) {
            //return false;
        }

        return lstVals.contains(number);
    }


    /**
     * 检查给定数是否在区间内(包含)
     *
     * @return
     */
    public static boolean inRange(int number, int min, int max) {
        int vl = number;
        int aa = min;
        int bb = max;
        return vl >= aa && vl <= bb;
    }

    /**
     * 检查给定数是否在区间内(包含)
     *
     * @return
     */
    public static boolean inRange(long number, long min, long max) {
        long vl = number;
        long aa = min;
        long bb = max;
        return vl >= aa && vl <= bb;
    }

    /**
     * 检查给定数是否在区间内(包含)
     *
     * @return
     */
    public static boolean inRange(float number, float min, float max) {
        float vl = number;
        float aa = min;
        float bb = max;
        return vl >= aa && vl <= bb;
    }

    /**
     * 检查给定数是否在区间内(不包含)
     *
     * @return
     */
    public static boolean outRange(int number, int min, int max) {
        int vl = number;
        int aa = min;
        int bb = max;
        if (vl < aa) {
            return true;
        }
        return vl > bb;
    }

    /**
     * 检查给定数是否在区间内(不包含)
     *
     * @return
     */
    public static boolean outRange(long number, long min, long max) {
        long vl = number;
        long aa = min;
        long bb = max;
        if (vl < aa) {
            return true;
        }
        return vl > bb;
    }

    /**
     * 检查给定数是否在区间内(不包含)
     *
     * @return
     */
    public static boolean outRange(float number, float min, float max) {
        float vl = number;
        float aa = min;
        float bb = max;
        if (vl < aa) {
            return true;
        }
        return vl > bb;
    }

    /**
     * 获取GUID
     *
     * @param notHasSepar - true:无分隔横杠；false:有分隔横杠
     * @return
     */
    public static String uuid(boolean notHasSepar) {
        String ret = UUID.randomUUID().toString();
        if (notHasSepar) {
            ret = ret.replace("-", "");
        }
        return ret;
    }

    /**
     * 获取GUID,无分隔横杠
     *
     * @return
     */
    public static String uuid() {
        return uuid(true);
    }

    /**
     * 车牌校验
     */
    public static boolean tstVPlate(String vplate) {
        vplate = vplate.toUpperCase();
        if (vplate != null && !vplate.isEmpty()) {

            String regexp = "^[临测京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z0-9]{1}([京津沪渝桂蒙宁新藏冀晋辽吉黑苏浙皖赣闽鲁粤鄂湘豫川云贵陕甘青琼])?[A-Z0-9]{4}[A-Z0-9挂学警港澳领应急临]{1,2}([A-Z0-9])?_([0-9]){1,2}$";//车牌正则
            if (vplate.contains("领")) {// 领事馆车辆
                // 辽14311领
                regexp = "^[京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼川贵云藏渝陕甘青宁新]{1}[A-Z0-9]{5}领_[0-9]{1,2}$";
            } else if (vplate.contains("使")) {// 使馆车辆
                // 166001使_黑
                regexp = "^[0-9]{3}[A-Z0-9]{3}使_[0-9]{1,2}$";
            } else if (vplate.indexOf("WJ") == 0) {// 武警车
                //{2013式
                // WJ京00001 - 5位全数字(内卫)
                // WJ浙3202B - 4位数字+(T:武警交通部队|D:武警水电部队|S:武警森林部队|H:武警黄金部队|B:武警边防部队|X:武警公安消防部队|J:公安警卫部队)
                //}
                //{2019式 - 采用2012式军牌格式
                // WJZ12345
                //}
                // regexp =  "^WJ[京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼川贵云藏渝陕甘青宁新]{1}[A-Z0-9]{5}_[0-9]{1,2}$";
                regexp = "^WJ[A-Z0-9]{6}_[0-9]{1,2}$";
            } else if (vplate.matches("^[A-Z]{1}[A-Z0-9_]*")) {// 军牌
                //{2012式 2016军改前
                // VA20708_3(白底黑字,首字母红)
                // 第一位:V:军委,4总部,大区直属|K:空军|H:海军|B:北京军区|S:沈阳军区|L:兰州军区|J:济南军区|N:南京军区|G:广州军区|C:成都军区
                // 第二位:A:司令部|B:政治部|C:后勤、联勤部|D:装备部|K:省军区、卫戍区|O:军车监理|R:院校|T:情报机构|V:战略导弹部队|Y:野战部队
                // 3~7 位:5位数字
                //}
                //{2012式 2016军改后
                // LB20708_3(白底黑字,首字母红)
                // 第一位:V:军委,4总部,大区直属|L:陆军|K:空军|H:海军|X:学院|S:省军区
                // 第二位:B:北部战区|D:东部战区|X:西部战区|Z:中部战区|N:南部战区|J:火箭军
                // 3~7 位:5位数字
                //}
                regexp = "^[A-Z]{1}[A-Z0-9]{6,}_[0-9]{1,2}$";
            } else if (vplate.contains("应急")) {// 应急救援车
                // 京X2345应急
                regexp = "^[京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼川贵云藏渝陕甘青宁新]{1}[A-Z]{1}[A-Z0-9]{4}应急_[0-9]{1,2}$";
            } else if (vplate.contains("警")) {// 警车、警车临牌
                // 蒙A0475警_3
                // 警013010临_n
                // 警013010_0
                // regexp =  "^[京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼川贵云藏渝陕甘青宁新]{1}[A-Z]{1}[A-Z0-9]{4}警_[0-9]{1,2}$";
                regexp = "^(([京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼川贵云藏渝陕甘青宁新]{1}[A-Z]{1}[A-Z0-9]{4}警)|(警[0-9A-Z]{6}临?))_[0-9]{1,2}$";
            } else if (vplate.contains("临")) {// 临牌
                // 津QP9996临
                regexp = "^[京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼川贵云藏渝陕甘青宁新]{1}[A-Z]{1}[A-Z0-9]{5}临_[0-9]{1,2}$";
            } else {// 普通车
                // 豫ABK539
                // 豫AD15702
                // 苏JU258挂
                // 京AF023学
                // 京C2002试
                // 豫AF85545_4
                regexp = "^[测京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼川贵云藏渝陕甘青宁新]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学试港澳]{1,2}_[0-9]{1,2}$";
            }
            return vplate.matches(regexp);
        }
        return false;
    }

    /**
     * 车牌校验(不校验车牌颜色)
     */
    public static boolean tstVPlateExcluCR(String vplate) {
        if (vplate != null && !vplate.isEmpty()) {

            vplate = split(vplate, 2, "_")[0] + "_0";

            return tstVPlate(vplate);
        }
        return false;
    }

    /**
     * 测试字符串
     *
     * @param num
     * @return
     */
    public static boolean tstSTR(String num, String regexp) {
        return num != null && num.matches(regexp);
    }

    /**
     * 比较两个字符串的相似度
     *
     * @param str
     * @param target
     * @return 百分数, 100 - 完全相同; 0 - 完全不同
     */
    public static int getSimRatio(String str, String target) {
        int[][] d; // 矩阵
        int n = str.length();
        int m = target.length();
        int i; // 遍历str的
        int j; // 遍历target的
        char ch1; // str的
        char ch2; // target的
        int temp; // 记录相同字符,在某个矩阵位置值的增量,不是0就是1
        if (n == 0 || m == 0) {
            return 0;
        }
        d = new int[n + 1][m + 1];
        for (i = 0; i <= n; i++) { // 初始化第一列
            d[i][0] = i;
        }

        for (j = 0; j <= m; j++) { // 初始化第一行
            d[0][j] = j;
        }

        for (i = 1; i <= n; i++) { // 遍历str
            ch1 = str.charAt(i - 1);
            // 去匹配target
            for (j = 1; j <= m; j++) {
                ch2 = target.charAt(j - 1);
                if (ch1 == ch2 || ch1 == ch2 + 32 || ch1 + 32 == ch2) {
                    temp = 0;
                } else {
                    temp = 1;
                }
                // 左边+1,上边+1, 左上角+temp取最小
                d[i][j] = Math.min(Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1), d[i - 1][j - 1] + temp);
            }
        }

        return (int) ((1 - (float) d[n][m] / Math.max(str.length(), target.length())) * 100F);
    }

    /**
     * 反转ArrayList中对象存储顺序
     */
    public static <T> ArrayList<T> reverseList(ArrayList<T> src) {
        ArrayList<T> des = new ArrayList<T>();
        for (int aa = src.size(); aa > 0; aa--) {
            des.add(src.get(aa - 1));
        }
        return des;
    }

    /**
     * 获取e.printStackTrace() 的具体信息，赋值给String 变量，并返回
     *
     * @param e Exception
     * @return e.printStackTrace() 中 的信息
     */
    public static String getStackTraceInfo(Throwable e) {
        StringWriter sw = null;
        PrintWriter pw = null;

        try {
            sw = new StringWriter();
            pw = new PrintWriter(sw);
            e.printStackTrace(pw);//将出错的栈信息输出到printWriter中
            pw.flush();
            sw.flush();

            return sw.toString();
        } catch (Exception ex) {
            return "发生错误";
        } finally {
            if (sw != null) {
                try {
                    sw.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            if (pw != null) {
                pw.close();
            }
        }
    }


}